/*
 * Traction.cpp
 *
 *  Created on: 6 mars 2011
 *      Author: Julien et Jerome
 */

#include "Traction.h"
#include "Rame.h"
#include "Element.h"
#include "Station.h"
#include <typeinfo>
#include <string.h>
#include "defines.h"
#define TEMPORISATION 6 //Nombre de secondes entre chaque déplacement d'une case, si la Traction a une vitesse de 0
#define VITESSE_MAX 3

void ajouterThread(pthread_t thread, int thread_type);

Traction::Traction() {
	// On bloque tous les signaux
	sigset_t set;
	sigfillset(&set);
	pthread_sigmask( SIG_BLOCK, & set, NULL );

	this->vitesse = 0;
	this->demarrerThread();
}

// Permet de connaitre quelle rame la traction fait avancer
void Traction::setRame(Rame* rame){
	this->rame = rame;
}

// Met en marche la traction à vitesse modéré
//TODO Mettre en place le systeme avec les signaux pour le départ et l'arret. :D
void Traction::seDeplacer(){
	while (this->estDemarree){
		Section * sec = this->rame->getSection();
		bool memeSection;
		cout <<"VITESSE : " << this->vitesse << endl;
		int pos = this->rame->getPosition();
		int numsec = sec->getNumero();

		cout << "Rame en position "<< pos << " de la section "<< numsec << endl;
		cout << "Je regarde au loin, que vois-je ?" << endl ;
		Element* actuel = sec->getElement(pos);

		Element* prochain = sec->emplacementSuivant(pos,this->vitesse,&memeSection);

		//---------------------------------------------------------------------
		// Gestion des feux de signalisation
		//---------------------------------------------------------------------
		int nbElementsSectionCourante = this->rame->getSection()->getTaille();
		// Si la Rame se situe sur le dernier Elément de la Section courante
		if(pos == nbElementsSectionCourante-1) {
			if(
				!memeSection && // Si le prochain Element appartient à une nouvelle Section
				!prochain->getSection()->getFeuSignalisation()->passageEstAutorise() // Si le feu n'autorise pas le passage
			) {
				int sigIntercepte;
				sigset_t signal;
				sigemptyset(&signal);
				sigaddset(&signal, SIGUSR1);

				// Si le feu est rouge, on s'arrête
				if(!prochain->getSection()->getFeuSignalisation()->passageEstAutorise()) {
					cout << "[!] Feu de signalisation perçu [!] : Passage interdit sur la section suivante => Le conducteur patiente le temps que le feu change d'état..." << endl;
//					this->stopper();
					// On se bloque jusqu'à recevoir un signal du feu
					int s = sigwait(&signal, &sigIntercepte);

					if(s == SIGUSR1) {
						cout << "[!] Feu de signalisation perçu [!] : Le feu est passé au vert => Le conducteur reprend son trajet." << endl;

//						this->avancer();
					}
				}
			}
		}

		// Si on doit changer de Section.
		if (!memeSection){
			cout << "Changement de section, actualisation des feux" << endl;
			this->rame->getSection()->getFeuSignalisation()->setPassageAutorise(true);

			Section * nouvelleSection = sec->getLigneTramway()->getSection(sec->getNumero()+1);
			this->rame->setSection(nouvelleSection);
			this->rame->setNumeroSection(nouvelleSection->getNumero());
			this->rame->getSection()->getFeuSignalisation()->setPassageAutorise(false);
		}
		// On s'efface de l'ancien élément.
		actuel->setContenu(NULL);

		prochain->setContenu(this->rame);

		this->rame->setPosition(prochain->getNumero());
		cout << "deplacement OK : position " << this->rame->getPosition() << " de la section " << this->rame->getNumeroSection() <<endl;

		// On vérifie si le prochain élément est une gare
		if (typeid(*prochain) == typeid(Station)){
			cout << "Gare détectée ! On s'arrête... Ou pas" <<endl;
			this->rame->gererArretStation();
			Station * station = dynamic_cast<Station*>(prochain);
			cout << station << endl;
			if (station->isTerminus()) {
				cout <<"Terminus, tout le monde descend, fin de la Rame."<<endl;
				this->stopper();
				this->arreterThread();
			}
		}

		sleep(TEMPORISATION - this->vitesse);
	}
}

// Reduit la vitesse de la traction (la vitesse minimale étant 1)
void Traction::ralentir(){
	if(this->vitesse > 1){
		this->vitesse --;
	}
	else{
		cout << "*** Vitesse minimale atteinte ***" << endl;
	}
}

// Arrete la traction
void Traction::stopper(){
	this->vitesse = 0;
	this->estDemarree = false;
}

void Traction::avancer(){
	this->vitesse = 1;
	this->estDemarree = true;
}

// Augmente la vitesse de déplacement de la traction (la vitesse maximale étant 3)
void Traction::accelerer(){
	cout << "Accélération" << endl;
	if(this->vitesse <= VITESSE_MAX){
		this->vitesse ++;
	}
	else{
		cout << "*** Vitesse maximale atteinte ***" << endl;
	}
}

Traction::~Traction() {
	// TODO Auto-generated destructor stub
}

// Fonction qui gere l'avancement, le ralentissement...
// Fonction statique (voir Traction.h)
void * Traction::threadRame(void * param) {

	// On recupère l'objet Traction passé en paramètre.
	Traction * tr = reinterpret_cast<Traction*>(param);
	while(tr->estActive){
		tr->seDeplacer(); // On lance la boucle de déplacement
	}
	return 0;
}



void Traction::demarrerThread() {
	cout << "Tentative de demarrage" << endl;
	// Creation du thread, le stockant dans l'attribut thread, et appelat la méthode statique threadRame.
	if (pthread_create(&thread, 0, Traction::threadRame, this) != 0) {
		cout << "/!\\ Echec dans fonction Traction::demarrerThread. /!\\"
				<< endl;
	} else {
		cout << "Thread créé" << endl;
		this->estActive = true;
	}
	cout << "Fin de tentative de demarrage" << endl;

	ajouterThread(thread,RAME);
}

void Traction::arreterThread(){
	cout << "Fin du thread Traction" <<endl;
	this->estActive = false;
}

// Retourne la vitesse actuelle de la traction.
int Traction::getVitesse() const {
	return this->vitesse;
}
